﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Globalization;
using System.ComponentModel;

namespace InductBase
{
    [TypeConverter(typeof(InductBase.Helpers.BasicTypesConverter))]    
    public struct Vec2
    {
        public double X;
        public double Y;
       
        public double Length
        {
            get
            {
                double oldLength = GetDistance(Vec2.Null, this);
                return oldLength;
            }
            set
            {
                double oldLength = GetDistance(Vec2.Null, this);
                X = (X / oldLength) * value;
                Y = (Y / oldLength) * value;
            }
        }

        public Vec2(Vec2 other) { X = other.X; Y = other.Y; }
        public Vec2(double x, double y) { this.X = x; this.Y = y; }
        public Vec2(PointF pnt) { this.X = pnt.X; this.Y = pnt.Y; }
        public Vec2(Point pnt) { this.X = pnt.X; this.Y = pnt.Y; }

        public static Vec2 LinearTween(Vec2 a, Vec2 b, Double part)
        {
            return a + (b - a) * part;
        }

        public static Vec2 operator +(Vec2 a, Vec2 b)
        {
            return new Vec2(a.X + b.X, a.Y + b.Y);
        }

        public static Vec2 operator -(Vec2 a, Vec2 b)
        {
            return new Vec2(a.X - b.X, a.Y - b.Y);
        }

        public static Vec2 operator -(Vec2 a)
        {
            return new Vec2(-a.X, -a.Y);
        }

        public static Vec2 operator *(Vec2 a, Vec2 b)
        {
            return new Vec2(a.X * b.X, a.Y * b.Y);
        }

        public static Vec2 operator /(Vec2 a, Vec2 b)
        {
            return new Vec2(a.X / b.X, a.Y / b.Y);
        }

        public static Vec2 operator %(Vec2 a, Vec2 b)
        {
            return new Vec2(a.X % b.X, a.Y % b.Y);
        }

        public static Vec2 operator *(Vec2 a, double b)
        {
            return new Vec2(a.X * b, a.Y * b);
        }

        public static Vec2 operator *(double b, Vec2 a)
        {
            return new Vec2(a.X * b, a.Y * b);
        }

        public static Vec2 operator /(Vec2 a, double b)
        {
            return new Vec2(a.X / b, a.Y / b);
        }

        public static bool operator ==(Vec2 a, Vec2 b)
        {
            if (ReferenceEquals(a, b)) return true;
            if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
                return ReferenceEquals(a, b);

            return a.X == b.X && a.Y == b.Y;
        }

        public static bool operator !=(Vec2 a, Vec2 b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(this, obj)) return true;
            if (ReferenceEquals(this, null) || ReferenceEquals(obj, null))
                return ReferenceEquals(this, obj);
            return this == (Vec2)obj;
        }

        public double GetDistanceTo(Vec2 a)
        {
            Vec2 b = this;
            return Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y));
        }

        public Vec2 Copy()
        {
            return new Vec2(this.X, this.Y);
        }
        public Vec2 Rotate(double angle)
        {
            angle = Math.PI * 2 * angle / 360.0;
            Vec2 res = new Vec2();
            double cos = Math.Cos(angle);
            double sin = Math.Sin(angle);
            res.X = this.X * cos - this.Y * sin;
            res.Y = this.X * sin + this.Y * cos;
            return res;
        }

        public Vec2 Normalized()
        {
            return this.Copy() / Length;
        }

        public Vec2 GetNegative()
        {
            return Vec2.Null - this;
        }

        public static double GetDistance(Vec2 a, Vec2 b)
        {
            return Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y));
        }

        public static Vec2 Null = new Vec2(0, 0);
        public static Vec2 Unit = new Vec2(1, 1);

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder("[", 10);
            sb.Append(X);
            sb.Append(";");
            sb.Append(Y);
            sb.Append("]");
            return sb.ToString().Replace(',', '.');
        }

        public static Vec2 Parse(string input)
        {
            input = input.Replace("[", "");
            input = input.Replace("]", "");
            input = input.Replace(",", ";");
            string[] args = input.Split(';');
            return new Vec2(Double.Parse(args[0], CultureInfo.InvariantCulture), Double.Parse(args[1], CultureInfo.InvariantCulture));
        }

        public bool IsAround(Vec2 other, double radius)
        {
            Vec2 diff = this - other;
            if (Math.Abs(diff.X) > radius) return false;
            if (Math.Abs(diff.Y) > radius) return false;
            if (diff.Length > radius) return false;
            return true;
        }

        public double GetRotation()
        {
            double result = Math.Atan2(Y, X) * 360 / (2 * Math.PI);
            return result;
        }

        public static explicit operator Vec2(String value)
        {
            return Vec2.Parse(value);
        }
    }
}
